﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using System.ComponentModel;

namespace ClipboardTranslator
{
    class IniDocument
    {
        Dictionary<string, NameValueCollection> data = 
            new Dictionary<string,NameValueCollection>();

        static readonly Regex regRemoveEmptyLines =
            new Regex
            (
                @"(\s*;[\d\D]*?\r?\n)+|\r?\n(\s*\r?\n)*", 
                RegexOptions.Multiline | RegexOptions.Compiled
            );

        static readonly Regex regParseIniData =
            new Regex
            (
                @"
                (?<IsSection>
                    ^\s*\[(?<SectionName>[^\]]+)?\]\s*$
                )
                |
                (?<IsKeyValue>
                    ^\s*(?<Key>[\d\D]*)?\s*\=\s*(?<Value>[\d\D]*)$
                )",
                RegexOptions.Compiled | 
                RegexOptions.IgnoreCase | 
                RegexOptions.IgnorePatternWhitespace
            );
        //^\s*(?<Key>[^(\s*\=\s*)]+)?\s*\=\s*(?<Value>[\d\D]*)$

        public IniDocument()
        {
            readIniData(null, null);
        }

        public IniDocument(string fileName)
            : this(fileName, Encoding.UTF8)
        { }

        public IniDocument(string fileName, Encoding encoding)
        {
            if (fileName != null && encoding != null && File.Exists(fileName))
            {
                using (FileStream fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Read, FileShare.None))
                {
                    string lastSection = string.Empty;
                    data.Add(lastSection, new NameValueCollection());
                    string iniData;
                    using
                    (
                        StreamReader reader =
                            new StreamReader(fs, encoding)
                    )
                        iniData = reader.ReadToEnd();

                    iniData = regRemoveEmptyLines.Replace(iniData, "\n");

                    string[] lines =
                        iniData.Split
                        (
                            new char[] { '\n' },
                            StringSplitOptions.RemoveEmptyEntries
                        );

                    foreach (string s in lines)
                    {
                        Match m = regParseIniData.Match(s);
                        if (m.Success)
                        {
                            if (m.Groups["IsSection"].Length > 0)
                            {
                                string sName =
                                    m.Groups["SectionName"].Value.
                                    ToLowerInvariant();

                                if (lastSection != sName)
                                {
                                    lastSection = sName;
                                    if (!data.ContainsKey(sName))
                                    {
                                        data.Add
                                        (
                                            sName,
                                            new NameValueCollection()
                                        );
                                    }
                                }
                            }
                            else if (m.Groups["IsKeyValue"].Length > 0)
                            {
                                data[lastSection].Add
                                (
                                    m.Groups["Key"].Value,
                                    m.Groups["Value"].Value
                                );
                            }
                        }
                    }
                }
            }
        }

        public IniDocument(Stream stream)
            : this(stream, Encoding.UTF8)
        {
        }

        public IniDocument(Stream stream, Encoding encoding)
        {
            if (stream == null || stream == Stream.Null)
                throw new ArgumentNullException("stream");
            if (encoding == null)
                throw new ArgumentNullException("encoding");

            readIniData(stream, encoding);
        }

        private void readIniData(Stream stream, Encoding encoding)
        {
        }

        public NameValueCollection this[string section]
        {
            get
            {
                section = section.ToLowerInvariant();
                if (!data.ContainsKey(section))
                    data.Add(section, new NameValueCollection());
                return data[section];
            }
        }

        public string this[string section, string key]
        {
            get
            {
                return this[section][key];
            }
            set
            {
                this[section][key] = value;
            }
        }

        public string[] SectionNames
        {
            get
            {
                string[] all = new string[data.Count];
                data.Keys.CopyTo(all, 0);
                return all;
            }
        }

        public string[] KeyNames(string section)
        {
            return this[section].AllKeys;
        }

        public string[] SectionValues(string section)
        {
            return this[section].GetValues(0);
        }

        public bool HasSection(string section)
        {
            return data.ContainsKey(section.ToLowerInvariant());
        }

        public bool HasKey(string section, string key)
        {
            return
                data.ContainsKey(section) &&
                !string.IsNullOrEmpty(data[section][key]);
        }

        public void Save(string fileName)
        {
            Save(fileName, Encoding.UTF8);
        }

        public void Save(string fileName, Encoding encoding)
        {
            using (FileStream fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
            {
                using (StreamWriter sw = new StreamWriter(fs, encoding))
                {
                    Dictionary<string, NameValueCollection>.Enumerator en =
                        data.GetEnumerator();
                    while (en.MoveNext())
                    {
                        KeyValuePair<string, NameValueCollection> cur =
                            en.Current;
                        if (!string.IsNullOrEmpty(cur.Key))
                        {
                            sw.WriteLine("[{0}]", cur.Key);
                        }
                        NameValueCollection col = cur.Value;
                        foreach (string key in col.Keys)
                        {
                            if (!string.IsNullOrEmpty(key))
                            {
                                string value = col[key];
                                if (!string.IsNullOrEmpty(value))
                                    sw.WriteLine("{0}={1}", key, value);
                            }
                        }
                    }
                    sw.Flush();
                }
            }
        }
    }
}
